﻿/*
 * Copyright (C) 2012, Jesus Requejo <jesus.requejo@gmail.com> 
 * Licensed under Microsoft Public License (Ms-PL)
 * http://www.microsoft.com/en-us/openness/licenses.aspx#MPL
*/


namespace LoaderServiceTray {
  
   using System;
   using System.Drawing;
   using System.Threading;
   using System.Windows.Forms;
   using Infrastructure;
   using WindowsFormsInfrastructure;

   public partial class MainForm : Form {

      private TrayHelper Tray { get; set; }
      private UacButtonHelper UacHelper { get; set; }
      private WindowsServicesManager ServiceManager { get; set; }
      private System.Threading.Timer Timer { get; set; }
      private SynchronizationContext Context { get; set; }
      private ApplicationServices AppServices { get; set; }
      private ApplicationsAlreadyLaunched ApplicationsAlreadyLaunched { get; set; }
      private uint SessionId { get; set; }
      private MessageWindow MessageWindow { get; set; }
      private bool UserAuthorizationIsDenied { get; set; }
      private int TimerDelay { get; set; }
      private readonly int normalDelay = 2500;
      private readonly int extendedDelay = 15000;

      public MainForm() {
         InitializeComponent();
         InitializeProperties();
         SetUpEvents();
         InitializeUI();
         InitializeMessageWindow();
         LaunchStartupApplications();
      }

      public void RestoreFromTrayAndBringToFront() {
         if (WindowState == FormWindowState.Minimized) {
            Tray.RestoreFromTray();
         }
      }

      private void InitializeMessageWindow() {
         string message = MagicStrings.BringServiceMonitorToFrontMessage;
         string windowName = MagicStrings.MonitorMessageWindowName;
         MessageWindow = new MessageWindow(this, windowName, message, RestoreFromTrayAndBringToFront);
      }

      private void StartUpdateTimer() {
         TimerDelay = normalDelay;
         if (Timer == null) {
            Timer = new System.Threading.Timer((o) => {
               PerformTimerAction();
               Timer.Change(TimerDelay, Timeout.Infinite);
            }, null, TimerDelay, Timeout.Infinite);
         }
      }

      private void StopUpdateTimer() {
         TimerDelay = extendedDelay;
      }

      private void PerformTimerAction() {
         LaunchStartupApplications();  // launch applications if we haven't managed to do it yet
         string serviceStatus = GetServiceStatus();
         string bootApplicationsStatus = GetStartupApplicationStatus();

         Context.Post((state) => {
            this.serviceStatusLabel.Text = serviceStatus;
            this.applicationStatusLabel.Text = bootApplicationsStatus;
         }, null);
      }

      private string GetStartupApplicationStatus() {
         if (UserAuthorizationIsDenied) {
            return "You don't have authorization to access startup applications currently";
         }

         if (!(ApplicationsAlreadyLaunched == LoaderServiceTray.ApplicationsAlreadyLaunched.True)) {
            ApplicationsAlreadyLaunched bootApplicationsAlreadyLaunched = AppServices.BootApplicationsAlreadyLaunched();
            return GetApplicationStatusHelper(bootApplicationsAlreadyLaunched);
         }
         // return cached value
         return GetApplicationStatusHelper(ApplicationsAlreadyLaunched);
      }

      private void LaunchStartupApplications() {
         var currentSessionId = GetActiveSessionId();
         if (currentSessionId == SessionId && !(ApplicationsAlreadyLaunched == LoaderServiceTray.ApplicationsAlreadyLaunched.True)) {
            UserAuthorizationIsDenied = false;
            try {
               bool success = AppServices.LaunchStartupApplications();
               ApplicationsAlreadyLaunched = success ? LoaderServiceTray.ApplicationsAlreadyLaunched.True : LoaderServiceTray.ApplicationsAlreadyLaunched.Failure;
               TimerDelay = success ? extendedDelay : normalDelay;
            }
            catch (UserNotAuthorizedException) {
               UserAuthorizationIsDenied = true;
               TimerDelay = extendedDelay;
            }
            catch (Exception) {
               // ignore all errors, the timer will automatically keep trying to launch the applications
               ApplicationsAlreadyLaunched = LoaderServiceTray.ApplicationsAlreadyLaunched.Failure;
            }
         }
      }

      private string GetApplicationStatusHelper(ApplicationsAlreadyLaunched applicationsLaunched) {
         string applicationStatus = String.Empty;

         switch (applicationsLaunched) {
            case ApplicationsAlreadyLaunched.True:
               applicationStatus = "Startup applications have been launched";
               break;
            case ApplicationsAlreadyLaunched.False:
               applicationStatus = "Startup applications have not yet been launched";
               break;
            case ApplicationsAlreadyLaunched.Unknown:
               applicationStatus = "Couldn't determine if startup applications have already been launched";
               break;
            case ApplicationsAlreadyLaunched.Failure:
               applicationStatus = "The last attempt to launch startup applications failed. Retrying...";
               break;
            default:
               break;
         }

         return applicationStatus;
      }

      private string GetServiceStatus() {
         var status = ServiceManager.GetServiceStatus();
         string statusDescription;
         switch (status) {
            case LoaderServiceStatus.Running:
               statusDescription = "The loader service is running normally";
               break;
            case LoaderServiceStatus.Other:
               statusDescription = "The loader service is not running";
               break;
            default:
               statusDescription = "The state of the loader service is unknown";
               break;
         }
         return statusDescription;
      }

      private uint GetActiveSessionId() {
         return ThirdParty.ApplicationLoader.GetActiveSessionId();
      }

      private void InitializeUI() {
         InitializeTrayContextMenu();
         UacHelper.DrawUacShieldOnButton();
         Tray.SendToTray();
         SetInitialLocation();
      }

      private void SetInitialLocation() {
         var taskbar = new WindowsFormsInfrastructure.Taskbar();
         var taskbarLocation = taskbar.Location;
         var screen = Screen.FromPoint(taskbarLocation);
         var taskbarWidth = taskbar.Size.Width;
         var taskbarHeight = taskbar.Size.Height;
         var taskbarX = taskbarLocation.X;
         var taskbarY = taskbarLocation.Y;

         var workingArea = screen.WorkingArea;
         int margin = 1;

         Point location = Point.Empty;

         switch (taskbar.Position) {
            case TaskbarPosition.Left:
               location = new Point(taskbarX + taskbarWidth + margin, workingArea.Height - Size.Height - margin);
               break;
            case TaskbarPosition.Top:
               location = new Point(taskbarX + workingArea.Width - Size.Width - margin, taskbarHeight + margin);
               break;
            case TaskbarPosition.Right:
               location = new Point(taskbarX - Size.Width - margin, workingArea.Height - Size.Height - margin);
               break;
            case TaskbarPosition.Bottom:
               location = new Point(workingArea.Width - Size.Width - margin, workingArea.Height - Size.Height - margin);
               break;
            default:
               return;
         }

         this.Location = location;
      }

      private void InitializeTrayContextMenu() {
         notifyIcon1.ContextMenuStrip = contextMenuStrip1;
         var uacShieldBitmap = UacIntegration.GetSmallShieldBitmap();
         contextMenuStrip1.Items.Add(launchServiceManagerButton.Text, uacShieldBitmap);
         contextMenuStrip1.Items[0].Click += UacHelper.launchServiceManagerButton_Click;
         contextMenuStrip1.Items.Add("Quit");
         contextMenuStrip1.Items[1].Click += (o, a) => { Close(); };
      }

      private void InitializeProperties() {

         Tray = new TrayHelper(this,
            (o, args) => { },
            () => { StopUpdateTimer(); },
            () => { StartUpdateTimer(); }
         );
         UacHelper = new UacButtonHelper(this);
         Context = SynchronizationContext.Current;
         ServiceManager = new WindowsServicesManager();
         AppServices = new LoaderServiceTray.ApplicationServices();
         ApplicationsAlreadyLaunched = LoaderServiceTray.ApplicationsAlreadyLaunched.Unknown;
         SessionId = GetActiveSessionId();
         TimerDelay = normalDelay;
      }

      private void SetUpEvents() {
         SetUpSendAndRestoreToTrayEvents();
         SetUpUacButtonClickedEvent();
      }

      private void SetUpSendAndRestoreToTrayEvents() {
         this.Resize += new EventHandler(Tray.Form1_Resize);
         this.notifyIcon1.MouseClick += Tray.notifyIcon1_MouseClickedInTrayIcon;
         this.FormClosing += Tray.Form1_FormClosing;
      }


      private void SetUpUacButtonClickedEvent() {
         this.launchServiceManagerButton.Click += UacHelper.launchServiceManagerButton_Click;
      }

      private class UacButtonHelper {
         private MainForm MainForm { get; set; }

         public UacButtonHelper(MainForm form) {
            MainForm = form;
         }

         public void DrawUacShieldOnButton() {
            var button = MainForm.launchServiceManagerButton;
            UacIntegration.DrawUacShieldOnButton(button);
         }

         public void launchServiceManagerButton_Click(object sender, EventArgs e) {
            try {
               MainForm.AppServices.LaunchServiceManager();
            }
            catch (Exception) {
               MessageBox.Show("Couldn't launch Service Manager", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
         }
      }
   }
}
